
package horde.patterns;

import horde.utils.Toolbox;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;

/**
 * @author Andres Picazo Cuesta
 */
public class Msg implements Serializable {

    // <editor-fold defaultstate="collapsed" desc="Attributes">

    /**
     * Format to be used on the call to the method 'getFormattedCreationTime'
     */
    public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String TYPE_REQUEST = "HORDE_REQUEST";
    public static final String TYPE_CONTROL = "HORDE_CONTROL";
    public static final String UNKNOWN = "PARAMETER_UNKNOWN";
    public static final int STANDARD = 0;
    public static final int STOP_REQUEST = 1; /* 0 reserved for no-control */
    public static final int DESTINATION_IS_A_GROUP = 2;
    public static int CURRENT_MSG_NUMBER = 1;

    private Date creationTime;
    private String source, destination;
    private String ipSource, ipDestination;
    private String type;
    private String id;
    private String hash;
    private Object [] trunk;
    private int control;
    private ArrayList<String> hopList;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Builders">
    public Msg (String src, String dst, String ipsrc, String ipdst, String type, Object ... trunk) {
        init (src,dst,ipsrc,ipdst,type,trunk);
        // <editor-fold defaultstate="collapsed" desc="old">
//        this.creationTime = Calendar.getInstance().getTime();
//        this.source = src;
//        this.destination = dst;
//        this.ipSource = ipsrc;
//        this.ipDestination = ipdst;
//        this.type = type;
//        this.control = 0;
//        this.hopList = new ArrayList<String>();
//        if (trunk == null) this.trunk = null;
//        else this.trunk = Arrays.copyOf(trunk, trunk.length);
//        if (trunk == null || trunk.length == 0) {
//            this.trunk = null;
//            this.id = ""+CURRENT_MSG_NUMBER++;
//        }
//        else {
//            this.trunk = Arrays.copyOf(trunk, trunk.length);
//            this.id = ""+CURRENT_MSG_NUMBER++ + "(" + trunk[0].toString() + ")";
//        }
//        this.hash = ""+creationTime.toString().hashCode();
        // </editor-fold>
    }

    public Msg (String src, String dst, String type, Object ... trunk) {
        init (src,dst,(new Toolbox()).getIP(), UNKNOWN, type, trunk);
        // <editor-fold defaultstate="collapsed" desc="old">
//        this.creationTime = Calendar.getInstance().getTime();
//        this.source = src;
//        this.destination = dst;
//        this.ipSource = (new Toolbox()).getIP();
//        this.ipDestination = UNKNOWN;
//        this.type = type;
//        this.control = 0;
//        this.hopList = new ArrayList<String>();
//        if (trunk == null || trunk.length == 0) {
//            this.trunk = null;
//            this.id = ""+CURRENT_MSG_NUMBER++;
//        }
//        else {
//            this.trunk = Arrays.copyOf(trunk, trunk.length);
//            this.id = ""+CURRENT_MSG_NUMBER++ + "(" + trunk[0].toString() + ")";
//        }
//        this.hash = ""+creationTime.toString().hashCode();
        // </editor-fold>
    }
    
    private void init (String src, String dst, String ipsrc, String ipdst, String type, Object ... trunk) {
        this.creationTime = Calendar.getInstance().getTime();
        this.source = src;
        this.destination = dst;
        this.ipSource = ipsrc;
        this.ipDestination = ipdst;
        this.type = type;
        this.control = 0;
        this.hopList = new ArrayList<String>();
        /* Hoplist first entry is the message source ip */
        this.hopList.add(ipsrc);
        if (trunk == null) this.trunk = null;
        else this.trunk = Arrays.copyOf(trunk, trunk.length);
        if (trunk == null || trunk.length == 0) {
            this.trunk = null;
            this.id = ""+CURRENT_MSG_NUMBER++;
        }
        else {
            this.trunk = Arrays.copyOf(trunk, trunk.length);
            this.id = ""+CURRENT_MSG_NUMBER++ + "(" + trunk[0].toString() + ")";
        }
        this.hash = ""+creationTime.toString().hashCode();
    }

    /**
     * Clone a message
     * @param msg Message to be cloned
     * @return Cloned message
     */
    public static Msg clone (Msg msg) {
        Msg clone = new Msg (msg.source, msg.destination, msg.ipSource, msg.ipDestination, msg.type, msg.trunk);
        clone.control = msg.control;
        return clone;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Getters and setters">
    /**
     * Gets the message creation timestamp
     * @return msg creation dat
     */
    public Date getCreationTime () {
        return creationTime;
    }

    /**
     * Gets the formatted msg creation timestamp, using the format given in the variable
     * DATE_FORMAT
     * @return Formatted string representing the creation time
     */
    public String getFormattedCreationTime () {
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
        return sdf.format(creationTime);
    }

    /**
     * Gets the destination of the message
     * @return destination id
     */
    public String getDestination() {
        return destination;
    }

    /**
     * Changes or sets the destination for the message
     * @param destination destination of the message
     */
    public void setDestination(String destination) {
        this.destination = destination;
    }

    /**
     * Gets the message destination ip
     * @return String representing destination ip
     */
    public String getIpDestination() {
        return ipDestination;
    }

    /**
     * Sets the destination ip
     * @param ipDestination ip of the destinatary
     */

    public void setIpDestination(String ipDestination) {
        this.ipDestination = ipDestination;
    }

    /**
     * Gets the ip of the message source
     * @return ip of the message source
     */
    public String getIpSource() {
        return ipSource;
    }

    /**
     * Sets the message source ip
     * @param ipSource if of the message source
     */
    public void setIpSource(String ipSource) {
        this.ipSource = ipSource;
    }

    /**
     * Gets the name of the message source
     * @return source id
     */
    public String getSource() {
        return source;
    }

    /**
     * Sets the message source id
     * @param source String name for the message source
     */
    public void setSource(String source) {
        this.source = source;
    }

    /**
     * Data sent as payload in the message. When used as messages for the horde automatons, the
     * first value is the message body to be processed
     * @return Object array containing the data payload
     */
    public Object[] getTrunk() {
        return trunk;
    }

    /**
     * Sets the data objects to be sent along the message
     * @param trunk Message payload (must be objects!!)
     */
    public void setTrunk(Object[] trunk) {
        this.trunk = trunk;
    }

    /**
     * Gets the type of the message
     * @return Message type, as defined in this class (static)
     */
    public String getType() {
        return type;
    }

    /**
     * Sets the message type (request or control)
     * @param type Message type
     */
    public void setType(String type) {
        this.type = type;
    }

    /**
     * Returns the hash value asociated to this message
     * @return Current hash value
     */
    public String getHash() {
        return hash;
    }

    /**
     * Sets the message hash value
     * @param hash Value to set the hash value
     */
    public void setHash(String hash) {
        this.hash = hash;
    }

    /**
     * Gets the message id
     * @return Message identificator
     */
    public String getId() {
        return id;
    }

    /**
     * Sets the message id
     * @param id Value to set the id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Gets the message control number, indicating the subtype of control message, for this value
     * to mean anything check first the return value from the method getType().
     * @return Control code value
     */
    public int getControl () {
        return control;
    }

    public void setControl (int value) {
        control = value;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Hop methods">
    /**
     * Adds a new hop to the hop list
     * @param ip Network ip for the hop
     */
    public void addHop (String ip) {
        hopList.add(ip);
    }

    /**
     * Gets the list with all hop this message has done
     * @return Hoplist
     */
    public ArrayList<String> getHopList () {
        return hopList;
    }

    /**
     * Tests if the given ip is contained in the hop list
     * @param ip Network direction to test
     * @return True if the hop has already been done, false otherwise
     */
    public boolean containsHop (String ip) {
        return hopList.contains(ip);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Default Messages">

    /**
     * Creates a standard control message with the stop request, asking the receiving agent
     * to stop
     * @param src Message source
     * @param dst Message destination
     * @return The already created and formatted message
     */
    public static Msg stop_request (String src, String dst) {
        Msg msg = new Msg (src,dst,Msg.TYPE_CONTROL);
        msg.control = Msg.STOP_REQUEST;
        return msg;
    }

    /* Message for single recipient */

    /**
     * Composes a new message of any type
     * @param src Message source
     * @param dst Message destination
     * @param type Message type
     * @param trunk Message payload
     * @return Formatted message ready to be sent
     */
    public static Msg compose(String src, String dst, String type, Object ... trunk) {
        return new Msg(src,dst,type,trunk);
    }

    /**
     * Composes a new message request
     * @param src Message source
     * @param dst Message destination
     * @param trunk Message payload (for the horde automatons first parameter being the message text)
     * @return Formatted message ready to be sent
     */
    public static Msg composeRequest (String src, String dst, Object ... trunk) {
        return new Msg(src,dst,Msg.TYPE_REQUEST,trunk);
    }

    /**
     * Composes a new message with a group as destination, instead of a single receiver
     * @param src Message source
     * @param dst Message destination
     * @param trunk Message payload (for the horde automatons first parameter being the message text)
     * @return Formatted message ready to be sent
     */
    public static Msg composeForGroup (String src, String dst, Object ... trunk) {
        Msg msg = new Msg(src,dst,Msg.TYPE_REQUEST,trunk);
        msg.control = Msg.DESTINATION_IS_A_GROUP;
        return msg;
    }

    // </editor-fold>
    
}
